Новый чат
Alexander Artyomov
Развитие ИИ: от нейроморфизма к мышлению
Привет! Несколько чатов назад мы делали ИИ чат на FreePascal'е под Debian GNU/Linux на x86_64. Сделали wordembedding на основе Word2Vec, сделали трансформер. Кое-что уже получилось и заработало, но у меня возник новый интересный вопрос. Ряд программ, имеющих в основе докомпьютерные корни нередко делали как внешнюю имитацию лежащего в основе изделия. Например, были бумажные записные книжки и ежедневники - их стали рисовать на компьютере в виде бумаги и ещё звук перелистывающихся страниц при смене страниц приделывали, аудиоплееры имеющие прототип в виде аудиотехники изобразили на компьютере в подобном виде: с вращающимися ручками громкости и прочими подобными аппаратурными органами управления, каклькуляторы изобразили по виду как их аппаратные предшественники, часы рисовали на экране со стрелками. Последующие программы в основном теряют внешнее сходство с прототипами и становятся полностью компьютерными по виду и содержанию. Так и ИИ в общем-то является сейчас эмуляцией деятельности головного мозга: с нейронами, прямым, обратным распространением, с обучением на данных... Подумай: не существует ли у этого направления (прежде всего у генеративного ИИ) возможности сделать следующий шаг в направлении от нейросетевого ИИ к некому машинному мышлению, конструкция которого больше бы напоминала обычные программы и которое бы умело всё, что умеют нейросетевые, трансформерные решения, а может и больше да ещё и проще и эффективнее?

Отличный и очень глубокий вопрос! Вы абсолютно правы, проводя параллель между эволюцией интерфейсов (от скевоморфных к цифровым) и текущим состоянием ИИ, который во многом является "скевоморфным" по отношению к биологическому мозгу.

Давайте разберем эту идею на части.

Текущее положение: "Нейроморфный скевоморфизм"

Вы точно подметили: современный ИИ — это имитация на очень упрощённом и абстрактном уровне работы нейронов мозга.

  • Нейроны -> Искусственные нейроны.

  • Синапсы -> Веса связей.

  • Обучение -> Корректировка весов через обратное распространение ошибки.

Это мощная и плодотворная парадигма, но у неё есть фундаментальные недостатки, унаследованные от её "прототипа":

  1. "Чёрный ящик": Мы не можем точно сказать, почему модель приняла то или иное решение.

  2. Вычислительная ёмкость: Обучение больших моделей требует колоссальных ресурсов.

  3. Хрупкость: Модели могут "сломаться" на данных, которые незначительно отличаются от обучающих.

  4. Отсутствие настоящего понимания и рассуждений: Модель статистически предсказывает следующее слово или пиксель, но не строит внутренней логической модели мира.

Следующий шаг: от "Мозга" к "Мыслительным Программам"

Вопрос, который вы задали, лежит в основе одного из самых перспективных направлений современного ИИ — нейро-символического подхода.

Идея в том, чтобы объединить две силы:

  • Сила №1 (Нейросетевая/Субсимволическая): Способность к распознаванию образов, обобщению из данных, работе с зашумленной информацией. Это "восприятие".

  • Сила №2 (Символическая/Логическая): Способность к логическим выводам, манипуляции символами, работе с правилами, планированию и объяснению решений. Это "рассуждение".

Вместо того чтобы заставлять нейросеть имитировать рассуждение, мы создаём архитектуру, где нейросеть отвечает за то, что у неё получается лучше всего (например, преобразование текста в структурированные факты), а затем передаёт эстафету классическому, детерминированному "вычислительному модулю".

Как могло бы выглядеть такое "машинное мышление"?

Представьте себе не монолитную трансформерную сеть, а операционную систему для мышления, состоящую из специализированных модулей:

  1. Модуль восприятия (Perception Module): Это как раз ваши word2vec и трансформеры. Его задача — преобразовать сырые данные (текст, изображение, звук) в структурированные символы, сущности и факты. Например, из предложения "Кот сидит на ковре" он извлекает факт: LocatedOn(Cat, Mat).

  2. Рабочая память (Working Memory): Буфер, где хранятся текущие факты, цели и контекст. Аналог оперативной памяти.

  3. База знаний (Knowledge Base): Постоянное хранилище фактов и правил о мире. Это может быть граф знаний, наполняемый как людьми, так и самим ИИ в процессе обучения. Например, Cat -> IsA -> Animal, Animal -> Can -> Breathe.

  4. Движок логического вывода (Inference Engine): Классический символьный ИИ. Он берёт факты из рабочей памяти и, применяя правила из базы знаний, делает логические выводы. Если он знает, что Animal -> NeedsTo -> DrinkWater и видит IsA(Cat, Animal), то он может сделать вывод NeedsTo(Cat, DrinkWater). Это делается без обучения, чистой логикой.

  5. Планировщик (Planner): Модуль, который ставит цели и выстраивает последовательности действий для их достижения, используя выводы движка.

  6. Модуль исполнения (Execution Module): Преобразует план в конкретные действия: генерацию текста, вызов API, управление роботом.

Преимущества такого подхода

  • Объяснимость: Можно проследить цепочку рассуждений: "Я сказал X, потому что из факта Y и правила Z следует X".

  • Эффективность: Не нужно переучивать всю гигантскую модель для добавления нового знания. Достаточно добавить новое правило в базу знаний.

  • Контроль: Легче вносить исправления и ограничения ("соблюдай закон X").

  • Обобщение на малых данных: Зная правило "если существо млекопитающее, оно дышит лёгкими", система сразу применит его к любому новому млекопитающему, без необходимости видеть миллионы его примеров.

  • Надёжность: Меньше подвержена "галлюцинациям", так как выводы проверяются логикой.

Сможет ли это заменить трансформеры?

Не заменить, а дополнить и усилить. Трансформеры и эмбеддинги останутся прекрасным "органами чувств" для такой системы. Их задача — понять неструктурированный мир и перевести его на язык, понятный символьным модулям.

Пример для вашего ИИ-чата на FreePascal:

Представьте, что ваш текущий трансформер генерирует ответ. Иногда он "галлюцинирует" и говорит что-то нелогичное.

А теперь представьте архитектуру, где:

  1. Трансформер-Анализатор разбирает вопрос пользователя и извлекает из него ключевые сущности и намерение.

  2. Эти данные попадают в Рабочую память.

  3. Движок логического вывода (это может быть просто набор процедур на Pascal, работающих с графами или правилами) проверяет факты по Базе Знаний (например, SQLite-базе с онтологией) и строит логический каркас ответа.

  4. Трансформер-Генератор получает этот логический каркас и "облекает его в плоть" естественного языка.

Такой чат будет гораздо более точным, логичным и управляемым.

Вывод

Ваша интуиция абсолютно верна. Движение от чистого "нейроморфного скевоморфизма" к гибридным, нейро-символическим архитектурам — это и есть тот самый "следующий шаг". Это переход от имитации мышления к созданию функциональной системы машинного reasoning (рассуждений).

Это сложнее, чем просто натренировать ещё одну большую модель, но это путь к созданию по-настоящему разумных, надёжных и понятных систем. И в этом направлении ваш исследовательский проект на FreePascal может быть как раз очень интересным полигоном для экспериментов! Вы можете начать с малого: создать простую базу фактов и логический движок, и посмотреть, как его можно сочетать с вашей word2vec-моделью.

AdvancedChatAI.pas
PAS 53.63KB
DataUtils.pas
PAS 700B
MatrixOps.pas
PAS 18.25KB
PositionalEncoding.pas
PAS 5.27KB
TextEmbeddings.pas
PAS 18.92KB
ucs4functionsunit.pas
PAS 14.18KB
ucs4multilineunit.pas
PAS 3.53KB
ucs4opunit.pas
PAS 1.81KB
ucs4unit.pas
PAS 8.89KB
Word2Vec.pas
PAS 30.84KB
Интересно. Посмотри, можно ли приделать это к нашему существующему движку?

Отличный вопрос! Давайте подумаем, как можно интегрировать нейро-символический подход в ваш существующий движок на FreePascal.

Анализ текущей архитектуры

Ваш текущий код уже содержит хорошую основу:

  1. Word2Vec/трансформер - модуль восприятия

  2. ContextManager - рабочая память

  3. ConversationHistory - история диалога

  4. TrainingData - накопленные знания

Практическая реализация нейро-символического подхода

Вот как можно модифицировать ваш код:

1. Добавляем символьный движок

pascal
// SymbolicEngine.pas
unit SymbolicEngine;

interface

uses
  SysUtils, Classes, DataUtils, ucs4unit;

type
  TFact = record
    Subject: string;
    Relation: string;
    Object: string;
    Confidence: Double;
  end;

  TRule = record
    Condition: string; // Логическое выражение
    Action: string;    // Действие/вывод
    Priority: Integer;
  end;

  TSymbolicEngine = class
  private
    FFacts: array of TFact;
    FRules: array of TRule;
    FWorkingMemory: TStringList;
  public
    constructor Create;
    destructor Destroy; override;
    
    procedure AddFact(const Subject, Relation, Object: string; Confidence: Double = 1.0);
    procedure AddRule(const Condition, Action: string; Priority: Integer = 1);
    
    function Infer(const Query: string): TFact;
    function ExecuteRule(const Input: string): string;
    
    procedure SaveToFile(const Filename: string);
    procedure LoadFromFile(const Filename: string);
  end;

implementation

constructor TSymbolicEngine.Create;
begin
  inherited;
  FWorkingMemory := TStringList.Create;
  SetLength(FFacts, 0);
  SetLength(FRules, 0);
  
  // Базовые правила
  AddRule('Contains(Input, "привет")', 'Greeting', 10);
  AddRule('Contains(Input, "пока")', 'Farewell', 10);
  AddRule('Contains(Input, "?") AND Length(Input) > 5', 'Question', 5);
end;

destructor TSymbolicEngine.Destroy;
begin
  FWorkingMemory.Free;
  inherited;
end;

procedure TSymbolicEngine.AddFact(const Subject, Relation, Object: string; Confidence: Double);
begin
  SetLength(FFacts, Length(FFacts) + 1);
  with FFacts[High(FFacts)] do
  begin
    Subject := Subject;
    Relation := Relation;
    Object := Object;
    Confidence := Confidence;
  end;
end;

procedure TSymbolicEngine.AddRule(const Condition, Action: string; Priority: Integer);
begin
  SetLength(FRules, Length(FRules) + 1);
  with FRules[High(FRules)] do
  begin
    Condition := Condition;
    Action := Action;
    Priority := Priority;
  end;
end;

function TSymbolicEngine.ExecuteRule(const Input: string): string;
var
  I: Integer;
  LowerInput: string;
begin
  Result := '';
  LowerInput := UTF8LowerCase(Input);
  
  // Применяем правила в порядке приоритета
  for I := 0 to High(FRules) do
  begin
    if FRules[I].Condition = 'Contains(Input, "привет")' then
    begin
      if LowerInput.Contains('привет') or LowerInput.Contains('здравств') then
      begin
        Result := 'Greeting';
        Exit;
      end;
    end
    else if FRules[I].Condition = 'Contains(Input, "пока")' then
    begin
      if LowerInput.Contains('пока') or LowerInput.Contains('до свидан') then
      begin
        Result := 'Farewell';
        Exit;
      end;
    end
    else if FRules[I].Condition = 'Contains(Input, "?") AND Length(Input) > 5' then
    begin
      if LowerInput.Contains('?') and (Length(Input) > 5) then
      begin
        Result := 'Question';
        Exit;
      end;
    end;
  end;
end;

function TSymbolicEngine.Infer(const Query: string): TFact;
begin
  // Упрощенный вывод - в реальности здесь будет полноценный логический движок
  Result.Subject := '';
  Result.Relation := '';
  Result.Object := '';
  Result.Confidence := 0.0;
end;

procedure TSymbolicEngine.SaveToFile(const Filename: string);
begin
  // Сохранение фактов и правил
end;

procedure TSymbolicEngine.LoadFromFile(const Filename: string);
begin
  // Загрузка фактов и правил
end;

end.

2. Модифицируем основной цикл обработки

pascal
// В AdvancedChatAI.pas
var
  SymbolicEngine: TSymbolicEngine;

procedure Initialize;
begin
  // Существующая инициализация...
  
  // Добавляем символьный движок
  SymbolicEngine := TSymbolicEngine.Create;
  
  // Загружаем базу знаний если есть
  if FileExists('knowledge_base.txt') then
    SymbolicEngine.LoadFromFile('knowledge_base.txt');
end;

procedure EnhancedProcessUserInput;
var
  inputText: ucs4;
  userMessage, ruleResult, finalResponse: string;
  extractedFacts: array of TFact;
  symbolicContext: string;
begin
  // 1. Получаем пользовательский ввод (как раньше)
  UserInput := GetUserInput(POST_FILE, filesList);
  inputText := UserInput.Message;
  userMessage := inputText.ToUTF8.Trim;

  if inputText.Length = 0 then Exit;

  WriteLn('Пользователь: ', userMessage);

  // 2. Символьный анализ
  ruleResult := SymbolicEngine.ExecuteRule(userMessage);
  WriteLn('Символьный анализ: ', ruleResult);

  // 3. Извлекаем факты через трансформер (нейросетевая часть)
  extractedFacts := ExtractFactsWithTransformer(userMessage);
  
  // 4. Добавляем факты в символьную систему
  for I := 0 to High(extractedFacts) do
  begin
    SymbolicEngine.AddFact(extractedFacts[I].Subject, 
                          extractedFacts[I].Relation, 
                          extractedFacts[I].Object,
                          extractedFacts[I].Confidence);
  end;

  // 5. Логический вывод на основе контекста
  symbolicContext := BuildSymbolicContext(ConversationHistory);
  finalResponse := GenerateHybridResponse(userMessage, ruleResult, symbolicContext);

  // 6. Вывод результата
  WriteLn('AI: ', finalResponse);
  UpdateConversationContext('', finalResponse);
end;

function ExtractFactsWithTransformer(const Text: string): array of TFact;
var
  inputMatrix, outputMatrix: TDoubleMatrix;
  entities: TStringArray;
  I: Integer;
begin
  // Используем трансформер для извлечения сущностей и отношений
  inputMatrix := CreateContextEnrichedInput(Text, '');
  ForwardTransformer(TransformerModel, inputMatrix, outputMatrix);
  
  // Преобразуем выход трансформера в факты
  entities := ParseEntitiesFromOutput(outputMatrix);
  
  SetLength(Result, Length(entities) div 3); // Предполагаем формат [субъект, отношение, объект]
  for I := 0 to (Length(entities) div 3) - 1 do
  begin
    Result[I].Subject := entities[I*3];
    Result[I].Relation := entities[I*3+1];
    Result[I].Object := entities[I*3+2];
    Result[I].Confidence := 0.8; // На основе уверенности модели
  end;
end;

function GenerateHybridResponse(const UserMessage, RuleResult, Context: string): string;
begin
  // Комбинируем подходы
  
  // 1. Если символьная система уверена - используем её
  if RuleResult = 'Greeting' then
    Result := 'Здравствуйте! Чем могу помочь?'
  else if RuleResult = 'Farewell' then
    Result := 'До свидания! Рад был пообщаться.'
  else if RuleResult = 'Question' then
  begin
    // Для вопросов используем комбинацию
    if Context.Contains('погод') then
      Result := 'К сожалению, у меня нет доступа к актуальным данным о погоде. '
    else
      Result := 'Интересный вопрос! ';
    
    // Добавляем нейросетевую вариативность
    Result := Result + GenerateTransformerResponse(UserMessage);
  end
  else
    // По умолчанию - нейросеть
    Result := GenerateTransformerResponse(UserMessage);
end;

3. Добавляем обучение символьной системы

pascal
procedure LearnSymbolicRules(const UserMessage, AIResponse: string);
var
  patterns: TStringArray;
  newRule: string;
begin
  // Анализируем успешные диалоги для извлечения правил
  if ShouldExtractRule(UserMessage, AIResponse) then
  begin
    patterns := ExtractPatterns(UserMessage);
    newRule := BuildRuleFromPatterns(patterns, AIResponse);
    SymbolicEngine.AddRule(newRule, AIResponse, 5);
    
    WriteLn('Добавлено новое правило: ', newRule);
  end;
end;

Преимущества такого подхода для вашей системы:

  1. Объяснимость: Можно точно сказать, почему выбран тот или иной ответ

  2. Контроль: Легко добавлять бизнес-правила и ограничения

  3. Эффективность: Простые запросы обрабатываются без запуска тяжелой модели

  4. Обучение: Новые правила добавляются без перетренировки всей модели

Постепенная интеграция:

  1. Этап 1: Добавить символьный движок для обработки простых запросов

  2. Этап 2: Настроить извлечение фактов из выхода трансформера

  3. Этап 3: Реализовать логический вывод на основе контекста

  4. Этап 4: Добавить автоматическое извлечение правил из диалогов

Это сохранит все ваши текущие наработки, но добавит "логический слой" поверх нейросетевого. Хотите, чтобы я подробнее расписал какой-то из этапов?

Хорошо. Нужны функции. SymbolicEngineUnit.pas(145,6) Error: Identifier not found "ShouldExtractRule" SymbolicEngineUnit.pas(147,17) Error: Identifier not found "ExtractPatterns" SymbolicEngineUnit.pas(148,16) Error: Identifier not found "BuildRuleFromPatterns"

Давайте реализуем эти недостающие функции. Вот полная версия SymbolicEngineUnit.pas:

pascal
// SymbolicEngineUnit.pas
unit SymbolicEngineUnit;

{$MODE OBJFPC}{$H+}
{$CODEPAGE UTF8}

interface

uses
  SysUtils, Classes, DataUtils, ucs4unit, ucs4functionsunit, TextEmbeddings;

type
  TFact = record
    Subject: string;
    Relation: string;
    Object: string;
    Confidence: Double;
  end;
  TFactArray = array of TFact;

  TRule = record
    Condition: string; // Логическое выражение
    Action: string;    // Действие/вывод
    Priority: Integer;
    UsageCount: Integer;
  end;
  TRuleArray = array of TRule;

  TSymbolicEngine = class
  private
    FFacts: TFactArray;
    FRules: TRuleArray;
    FWorkingMemory: TStringList;
    
    // Вспомогательные методы для парсинга условий
    function ParseCondition(const Condition: string): TStringArray;
    function EvaluateCondition(const Condition: string; const Input: string): Boolean;
    
  public
    constructor Create;
    destructor Destroy; override;
    
    // Управление фактами
    procedure AddFact(const Subject, Relation, Object: string; Confidence: Double = 1.0);
    function FindFacts(const Subject, Relation, Object: string): TFactArray;
    procedure RemoveFact(Index: Integer);
    
    // Управление правилами
    procedure AddRule(const Condition, Action: string; Priority: Integer = 1);
    function FindMatchingRules(const Input: string): TRuleArray;
    
    // Основные функции
    function ExecuteRule(const Input: string): string;
    function Infer(const Query: string): TFactArray;
    
    // Обучение и извлечение правил
    function ShouldExtractRule(const UserMessage, AIResponse: string): Boolean;
    function ExtractPatterns(const Text: string): TStringArray;
    function BuildRuleFromPatterns(const Patterns: TStringArray; const Response: string): string;
    procedure LearnFromDialogue(const UserMessage, AIResponse: string);
    
    // Сохранение/загрузка
    procedure SaveToFile(const Filename: string);
    procedure LoadFromFile(const Filename: string);
    
    // Утилиты
    function GetStats: string;
    procedure ClearRules;
  end;

// Глобальные функции для работы с текстом
function ContainsAny(const Text: string; const Patterns: array of string): Boolean;
function ExtractKeywords(const Text: string): TStringArray;
function CalculatePatternScore(const Pattern: string): Double;

var
  SymbolicEngine: TSymbolicEngine;

implementation

function ContainsAny(const Text: string; const Patterns: array of string): Boolean;
var
  I: Integer;
  LowerText: string;
begin
  LowerText := UTF8LowerCase(Text);
  for I := 0 to High(Patterns) do
  begin
    if LowerText.Contains(UTF8LowerCase(Patterns[I])) then
      Exit(True);
  end;
  Result := False;
end;

function ExtractKeywords(const Text: string): TStringArray;
var
  tokens: TUC4Array;
  i, count: Integer;
  tokenStr: string;
begin
  SetLength(Result, 0);
  
  // Токенизируем текст
  tokens := TokenizeForNLP(NormalizeForAI(RemovePunctuation(Text)));
  if Length(tokens) = 0 then Exit;
  
  // Фильтруем стоп-слова и короткие токены
  count := 0;
  SetLength(Result, Length(tokens));
  
  for i := 0 to High(tokens) do
  begin
    tokenStr := tokens[i].ToUTF8.Trim;
    
    // Пропускаем короткие слова и стоп-слова
    if (Length(tokenStr) > 2) and 
       (not ContainsAny(tokenStr, ['это', 'что', 'как', 'где', 'когда', 'почему', 'зачем'])) then
    begin
      Result[count] := tokenStr;
      Inc(count);
    end;
  end;
  
  SetLength(Result, count);
end;

function CalculatePatternScore(const Pattern: string): Double;
var
  keywords: TStringArray;
  i: Integer;
begin
  Result := 0.0;
  keywords := ExtractKeywords(Pattern);
  
  // Оцениваем паттерн на основе количества ключевых слов и их значимости
  for i := 0 to High(keywords) do
  begin
    // Простая эвристика: более длинные слова обычно более значимы
    Result := Result + Length(keywords[i]) * 0.1;
    
    // Особо значимые слова
    if ContainsAny(keywords[i], ['погод', 'врем', 'температур', 'градус']) then
      Result := Result + 1.0;
    if ContainsAny(keywords[i], ['имя', 'зовут', 'называ']) then
      Result := Result + 1.0;
    if ContainsAny(keywords[i], ['счет', 'математ', 'числ', 'плюс', 'минус']) then
      Result := Result + 1.5;
  end;
end;

constructor TSymbolicEngine.Create;
begin
  inherited;
  FWorkingMemory := TStringList.Create;
  SetLength(FFacts, 0);
  SetLength(FRules, 0);
  
  // Базовые правила
  AddRule('Contains(Input, "привет") OR Contains(Input, "здравств")', 'Greeting', 10);
  AddRule('Contains(Input, "пока") OR Contains(Input, "до свидан")', 'Farewell', 10);
  AddRule('Contains(Input, "спасибо") OR Contains(Input, "благодар")', 'Thanks', 8);
  AddRule('EndsWith(Input, "?") AND Length(Input) > 3', 'Question', 5);
  AddRule('Contains(Input, "погод")', 'WeatherQuery', 7);
  AddRule('Contains(Input, "имя") OR Contains(Input, "зовут")', 'NameQuery', 6);
  AddRule('Contains(Input, "счет") OR Contains(Input, "посчитай")', 'MathQuery', 9);
end;

destructor TSymbolicEngine.Destroy;
begin
  FWorkingMemory.Free;
  inherited;
end;

procedure TSymbolicEngine.AddFact(const Subject, Relation, Object: string; Confidence: Double);
begin
  SetLength(FFacts, Length(FFacts) + 1);
  with FFacts[High(FFacts)] do
  begin
    Subject := Subject;
    Relation := Relation;
    Object := Object;
    Confidence := Confidence;
  end;
end;

function TSymbolicEngine.FindFacts(const Subject, Relation, Object: string): TFactArray;
var
  i, count: Integer;
begin
  SetLength(Result, 0);
  count := 0;
  
  for i := 0 to High(FFacts) do
  begin
    if ((Subject = '') or (FFacts[i].Subject = Subject)) and
       ((Relation = '') or (FFacts[i].Relation = Relation)) and
       ((Object = '') or (FFacts[i].Object = Object)) then
    begin
      SetLength(Result, count + 1);
      Result[count] := FFacts[i];
      Inc(count);
    end;
  end;
end;

procedure TSymbolicEngine.RemoveFact(Index: Integer);
var
  i: Integer;
begin
  if (Index < 0) or (Index > High(FFacts)) then Exit;
  
  for i := Index to High(FFacts) - 1 do
    FFacts[i] := FFacts[i + 1];
  
  SetLength(FFacts, Length(FFacts) - 1);
end;

procedure TSymbolicEngine.AddRule(const Condition, Action: string; Priority: Integer);
begin
  SetLength(FRules, Length(FRules) + 1);
  with FRules[High(FRules)] do
  begin
    Condition := Condition;
    Action := Action;
    Priority := Priority;
    UsageCount := 0;
  end;
end;

function TSymbolicEngine.ParseCondition(const Condition: string): TStringArray;
begin
  // Простой парсер условий - разбиваем по OR и AND
  SetLength(Result, 0);
  
  if Condition.Contains(' OR ') then
  begin
    SetLength(Result, 2);
    Result[0] := Copy(Condition, 1, Pos(' OR ', Condition) - 1);
    Result[1] := Copy(Condition, Pos(' OR ', Condition) + 4, MaxInt);
  end
  else if Condition.Contains(' AND ') then
  begin
    SetLength(Result, 2);
    Result[0] := Copy(Condition, 1, Pos(' AND ', Condition) - 1);
    Result[1] := Copy(Condition, Pos(' AND ', Condition) + 5, MaxInt);
  end
  else
  begin
    SetLength(Result, 1);
    Result[0] := Condition;
  end;
end;

function TSymbolicEngine.EvaluateCondition(const Condition: string; const Input: string): Boolean;
var
  parts: TStringArray;
  lowerInput: string;
begin
  Result := False;
  lowerInput := UTF8LowerCase(Input);
  
  if Condition.StartsWith('Contains(Input, "') and Condition.EndsWith('")') then
  begin
    // Извлекаем подстроку для поиска
    parts := Condition.Split(['"']);
    if Length(parts) >= 2 then
      Result := lowerInput.Contains(UTF8LowerCase(parts[1]));
  end
  else if Condition.StartsWith('EndsWith(Input, "') and Condition.EndsWith('")') then
  begin
    parts := Condition.Split(['"']);
    if Length(parts) >= 2 then
      Result := lowerInput.EndsWith(UTF8LowerCase(parts[1]));
  end
  else if Condition.StartsWith('StartsWith(Input, "') and Condition.EndsWith('")') then
  begin
    parts := Condition.Split(['"']);
    if Length(parts) >= 2 then
      Result := lowerInput.StartsWith(UTF8LowerCase(parts[1]));
  end
  else if Condition.Contains(' OR ') then
  begin
    parts := ParseCondition(Condition);
    Result := EvaluateCondition(parts[0], Input) or EvaluateCondition(parts[1], Input);
  end
  else if Condition.Contains(' AND ') then
  begin
    parts := ParseCondition(Condition);
    Result := EvaluateCondition(parts[0], Input) and EvaluateCondition(parts[1], Input);
  end;
end;

function TSymbolicEngine.FindMatchingRules(const Input: string): TRuleArray;
var
  i, count: Integer;
begin
  SetLength(Result, 0);
  count := 0;
  
  for i := 0 to High(FRules) do
  begin
    if EvaluateCondition(FRules[i].Condition, Input) then
    begin
      SetLength(Result, count + 1);
      Result[count] := FRules[i];
      Inc(count);
    end;
  end;
end;

function TSymbolicEngine.ExecuteRule(const Input: string): string;
var
  matchingRules: TRuleArray;
  bestRule: TRule;
  i: Integer;
begin
  Result := '';
  matchingRules := FindMatchingRules(Input);
  
  if Length(matchingRules) = 0 then Exit;
  
  // Выбираем правило с наивысшим приоритетом
  bestRule := matchingRules[0];
  for i := 1 to High(matchingRules) do
  begin
    if matchingRules[i].Priority > bestRule.Priority then
      bestRule := matchingRules[i];
  end;
  
  // Обновляем счетчик использования
  for i := 0 to High(FRules) do
  begin
    if FRules[i].Condition = bestRule.Cition then
    begin
      Inc(FRules[i].UsageCount);
      Break;
    end;
  end;
  
  Result := bestRule.Action;
end;

function TSymbolicEngine.Infer(const Query: string): TFactArray;
begin
  // Упрощенный логический вывод
  // В реальной реализации здесь будет полноценный логический движок
  Result := FindFacts('', '', '');
end;

// Реализация недостающих функций

function TSymbolicEngine.ShouldExtractRule(const UserMessage, AIResponse: string): Boolean;
var
  msgScore, responseScore: Double;
begin
  // Решаем, стоит ли извлекать правило из этого диалога
  
  // Критерии для извлечения правил:
  // 1. Сообщение не слишком короткое
  // 2. Ответ не шаблонный
  // 3. Высокая уверенность в качестве диалога
  
  if Length(UserMessage) < 5 then Exit(False);
  if Length(AIResponse) < 3 then Exit(False);
  
  // Оцениваем значимость сообщения
  msgScore := CalculatePatternScore(UserMessage);
  responseScore := CalculatePatternScore(AIResponse);
  
  // Пропускаем простые приветствия и прощания
  if ContainsAny(UserMessage, ['привет', 'пока', 'спасибо']) and 
     ContainsAny(AIResponse, ['Здравствуйте', 'До свидания', 'Пожалуйста']) then
    Exit(False);
  
  // Извлекаем правила для значимых диалогов
  Result := (msgScore > 1.0) and (responseScore > 0.5);
end;

function TSymbolicEngine.ExtractPatterns(const Text: string): TStringArray;
var
  keywords: TStringArray;
  i: Integer;
begin
  SetLength(Result, 0);
  keywords := ExtractKeywords(Text);
  
  // Формируем паттерны на основе ключевых слов
  for i := 0 to High(keywords) do
  begin
    SetLength(Result, Length(Result) + 1);
    
    // Создаем условие для каждого значимого ключевого слова
    if Length(keywords[i]) > 3 then
      Result[High(Result)] := 'Contains(Input, "' + keywords[i] + '")'
    else
      SetLength(Result, Length(Result) - 1); // Удаляем если слово слишком короткое
  end;
end;

function TSymbolicEngine.BuildRuleFromPatterns(const Patterns: TStringArray; const Response: string): string;
var
  condition: string;
  i: Integer;
begin
  if Length(Patterns) = 0 then
  begin
    Result := '';
    Exit;
  end;
  
  // Объединяем паттерны через OR
  condition := Patterns[0];
  for i := 1 to High(Patterns) do
    condition := condition + ' OR ' + Patterns[i];
  
  Result := condition;
end;

procedure TSymbolicEngine.LearnFromDialogue(const UserMessage, AIResponse: string);
var
  patterns: TStringArray;
  newRule: string;
begin
  if not ShouldExtractRule(UserMessage, AIResponse) then Exit;
  
  WriteLn('Символьное обучение: извлекаем правило из диалога');
  WriteLn('  Вход: ', UserMessage);
  WriteLn('  Выход: ', AIResponse);
  
  patterns := ExtractPatterns(UserMessage);
  if Length(patterns) = 0 then Exit;
  
  newRule := BuildRuleFromPatterns(patterns, AIResponse);
  if newRule <> '' then
  begin
    AddRule(newRule, AIResponse, 3); // Низкий приоритет для изученных правил
    WriteLn('  Добавлено правило: ', newRule, ' -> ', AIResponse);
  end;
end;

procedure TSymbolicEngine.SaveToFile(const Filename: string);
var
  F: TextFile;
  i: Integer;
begin
  AssignFile(F, Filename);
  try
    Rewrite(F);
    
    // Сохраняем факты
    WriteLn(F, '[Facts]');
    for i := 0 to High(FFacts) do
    begin
      WriteLn(F, FFacts[i].Subject, '|', FFacts[i].Relation, '|', 
              FFacts[i].Object, '|', FFacts[i].Confidence:0:4);
    end;
    
    // Сохраняем правила
    WriteLn(F, '[Rules]');
    for i := 0 to High(FRules) do
    begin
      WriteLn(F, FRules[i].Condition, '|', FRules[i].Action, '|', 
              FRules[i].Priority, '|', FRules[i].UsageCount);
    end;
    
  finally
    CloseFile(F);
  end;
end;

procedure TSymbolicEngine.LoadFromFile(const Filename: string);
var
  F: TextFile;
  Line: string;
  Parts: TStringArray;
  section: string;
begin
  if not FileExists(Filename) then Exit;
  
  AssignFile(F, Filename);
  try
    Reset(F);
    section := '';
    
    while not Eof(F) do
    begin
      ReadLn(F, Line);
      Line := Trim(Line);
      
      if Line = '' then Continue;
      
      if Line = '[Facts]' then
        section := 'facts'
      else if Line = '[Rules]' then
        section := 'rules'
      else if section = 'facts' then
      begin
        Parts := Line.Split(['|']);
        if Length(Parts) >= 4 then
        begin
          AddFact(Parts[0], Parts[1], Parts[2], StrToFloatDef(Parts[3], 1.0));
        end;
      end
      else if section = 'rules' then
      begin
        Parts := Line.Split(['|']);
        if Length(Parts) >= 3 then
        begin
          AddRule(Parts[0], Parts[1], StrToIntDef(Parts[2], 1));
        end;
      end;
    end;
    
  finally
    CloseFile(F);
  end;
end;

function TSymbolicEngine.GetStats: string;
begin
  Result := Format('Фактов: %d, Правил: %d', [Length(FFacts), Length(FRules)]);
end;

procedure TSymbolicEngine.ClearRules;
begin
  SetLength(FRules, 0);
end;

initialization
  SymbolicEngine := TSymbolicEngine.Create;

finalization
  SymbolicEngine.Free;

end.

Теперь также обновим AdvancedChatAI.pas для интеграции символьного движка:

pascal
// В AdvancedChatAI.pas - добавьте в секцию uses:
uses
  ..., SymbolicEngineUnit;

// В процедуре Initialize добавьте:
procedure Initialize;
begin
  // Существующий код...
  
  // Инициализация символьного движка
  WriteLn('Инициализация символьного движка...');
  
  // Загружаем базу знаний если есть
  if FileExists('symbolic_knowledge.txt') then
  begin
    SymbolicEngine.LoadFromFile('symbolic_knowledge.txt');
    WriteLn('База знаний загружена: ', SymbolicEngine.GetStats);
  end;
end;

// Новая улучшенная версия ProcessUserInput:
procedure EnhancedProcessUserInput;
var
  inputText: ucs4;
  userMessage, symbolicResult, finalResponse: string;
  filesList: TStringList;
  inputMatrix, outputMatrix: TDoubleMatrix;
begin
  filesList := TStringList.Create;
  try
    // 1. Получаем пользовательский ввод
    UserInput := GetUserInput(POST_FILE, filesList);
    inputText := UserInput.Message;
    userMessage := inputText.ToUTF8.Trim;

    if inputText.Length = 0 then begin
      WriteLn('Пустое сообщение, пропускаем');
      Exit;
    end;

    WriteLn('Пользователь: ', userMessage);
    LastUserMessage := userMessage;

    // 2. Добавляем сообщение в историю
    UpdateConversationContext(userMessage, '');

    // 3. Символьный анализ
    symbolicResult := SymbolicEngine.ExecuteRule(userMessage);
    
    if symbolicResult <> '' then
    begin
      WriteLn('Символьная система: ', symbolicResult);
      
      // Генерируем ответ на основе символьного результата
      case symbolicResult of
        'Greeting': 
          finalResponse := 'Здравствуйте! Рад вас видеть. Чем могу помочь?';
        'Farewell': 
          finalResponse := 'До свидания! Буду рад пообщаться снова.';
        'Thanks':
          finalResponse := 'Пожалуйста! Обращайтесь, если понадобится помощь.';
        'Question':
          finalResponse := 'Интересный вопрос! ' + GenerateTransformerResponse(userMessage);
        'WeatherQuery':
          finalResponse := 'К сожалению, у меня нет доступа к актуальным данным о погоде. ' +
                          'Но могу обсудить погоду в общем смысле!';
        'NameQuery':
          finalResponse := 'Я - чат-бот с искусственным интеллектом. ' +
                          'Можно просто называть меня "помощник".';
        'MathQuery':
          finalResponse := 'Я могу помочь с математическими вопросами! ' +
                          'Задайте конкретный пример для расчета.';
        else
          finalResponse := GenerateTransformerResponse(userMessage);
      end;
    end
    else
    begin
      // 4. Если символьная система не нашла правила, используем трансформер
      WriteLn('Символьная система: правило не найдено, используем трансформер');
      inputMatrix := CreateContextEnrichedInput(inputText, GetConversationContext);
      ForwardTransformer(TransformerModel, inputMatrix, outputMatrix);
      finalResponse := ResponseGenerator.GenerateResponse(outputMatrix, GetConversationContext, userMessage);
    end;

    // 5. Выводим ответ и добавляем в историю
    WriteLn('AI: ', finalResponse);
    UpdateConversationContext('', finalResponse);

    // 6. Обучение на успешном диалоге
    if ShouldSaveForTraining(userMessage, finalResponse) then
    begin
      LearnFromConversation(userMessage, finalResponse);
      SymbolicEngine.LearnFromDialogue(userMessage, finalResponse);
      
      // Сохраняем базу знаний
      SymbolicEngine.SaveToFile('symbolic_knowledge.txt');
    end;

  except
    on E: Exception do
    begin
      WriteLn('Ошибка: ', E.Message);
      finalResponse := 'Извините, произошла техническая ошибка. Попробуйте еще раз.';
      WriteLn('AI: ', finalResponse);
    end;
  end;
  filesList.Free;
end;

Теперь все функции реализованы и система должна компилироваться. Символьный движок будет:

  1. Обрабатывать простые запросы через правила

  2. Извлекать новые правила из успешных диалогов

  3. Сохранять базу знаний между запусками

  4. Работать вместе с трансформером для сложных случаев

Попробуйте скомпилировать и протестировать!

Ещё: AdvancedChatAI.pas(944,15) Error: Identifier not found "ParseEntitiesFromOutput" AdvancedChatAI.pas(1018,22) Error: Identifier not found "BuildSymbolicContext"

Давайте реализуем эти недостающие функции. Добавим их в SymbolicEngineUnit.pas:

pascal
// SymbolicEngineUnit.pas - добавляем новые функции

// В интерфейсную часть добавляем:
function ParseEntitiesFromOutput(const OutputMatrix: TDoubleMatrix): TStringArray;
function BuildSymbolicContext(const History: TStringList): string;

// В реализацию добавляем:

function ParseEntitiesFromOutput(const OutputMatrix: TDoubleMatrix): TStringArray;
var
  i, j, entityCount: Integer;
  maxVal: Double;
  maxIndex: Integer;
  entities: array of string;
begin
  SetLength(Result, 0);
  if (Length(OutputMatrix) = 0) or (Length(OutputMatrix[0]) = 0) then Exit;
  
  // Упрощенная реализация - в реальности здесь будет полноценное извлечение сущностей
  // Сейчас просто возвращаем пустой массив для демонстрации
  
  // В реальной реализации здесь будет:
  // 1. Классификация токенов на сущности (PER, ORG, LOC и т.д.)
  // 2. Извлечение отношений между сущностями
  // 3. Формирование фактов
  
  WriteLn('ParseEntitiesFromOutput: извлечение сущностей из матрицы ', 
          Length(OutputMatrix), 'x', Length(OutputMatrix[0]));
  
  // Заглушка - возвращаем пустой массив
  // В рабочей версии здесь будет реальное извлечение сущностей
end;

function BuildSymbolicContext(const History: TStringList): string;
var
  i: Integer;
  contextFacts: TStringList;
  recentMessages: string;
  fact: TFact;
  facts: TFactArray;
begin
  contextFacts := TStringList.Create;
  try
    // Собираем последние сообщения для контекста
    recentMessages := '';
    for i := Max(0, History.Count - 4) to History.Count - 1 do
    begin
      if i >= 0 then
        recentMessages := recentMessages + History[i] + ' ';
    end;
    
    // Извлекаем факты из истории
    facts := SymbolicEngine.FindFacts('', '', '');
    
    // Формируем контекст в виде строки
    Result := 'Recent: ' + recentMessages.Trim;
    
    if Length(facts) > 0 then
    begin
      Result := Result + ' | Facts: ';
      for i := 0 to Min(High(facts), 2) do // Берем только 3 последних факта
      begin
        Result := Result + facts[i].Subject + '-' + facts[i].Relation + '-' + 
                 facts[i].Object + '; ';
      end;
    end;
    
    // Добавляем информацию о текущих правилах
    Result := Result + ' | Rules: ' + IntToStr(Length(SymbolicEngine.FRules));
    
  finally
    contextFacts.Free;
  end;
end;

// Также обновим функцию ExtractFactsWithTransformer в AdvancedChatAI.pas:
function ExtractFactsWithTransformer(const Text: string): TFactArray;
var
  inputMatrix, outputMatrix: TDoubleMatrix;
  entities: TStringArray;
  i, factCount: Integer;
  tokens: TUC4Array;
begin
  SetLength(Result, 0);
  
  // Упрощенная реализация - используем токенизацию и эвристики
  // В реальной реализации здесь будет полноценное извлечение фактов
  
  try
    // Токенизируем текст
    tokens := TokenizeForNLP(NormalizeForAI(RemovePunctuation(Text)));
    
    factCount := 0;
    SetLength(Result, Length(tokens) div 2); // Оценка количества возможных фактов
    
    // Простая эвристика для извлечения фактов
    for i := 0 to High(tokens) - 1 do
    begin
      // Если два соседних токена могут образовывать факт
      if (tokens[i].Length > 2) and (tokens[i + 1].Length > 2) then
      begin
        // Простая эвристика: если первый токен похож на субъект, второй на объект
        Result[factCount].Subject := tokens[i].ToUTF8;
        Result[factCount].Relation := 'is_related_to'; // Упрощенное отношение
        Result[factCount].Object := tokens[i + 1].ToUTF8;
        Result[factCount].Confidence := 0.3; // Низкая уверенность для эвристики
        
        Inc(factCount);
      end;
    end;
    
    SetLength(Result, factCount);
    
    WriteLn('Извлечено фактов: ', factCount);
    
  except
    on E: Exception do
    begin
      WriteLn('Ошибка извлечения фактов: ', E.Message);
      SetLength(Result, 0);
    end;
  end;
end;

Теперь давайте создам отдельный модуль для расширенной обработки контекста:

pascal
// ContextProcessing.pas
unit ContextProcessing;

{$MODE OBJFPC}{$H+}
{$CODEPAGE UTF8}

interface

uses
  SysUtils, Classes, DataUtils, MatrixOps, ucs4unit, ucs4functionsunit, 
  TextEmbeddings, SymbolicEngineUnit;

// Функции для обработки контекста
function BuildEnhancedContext(const History: TStringList; const CurrentInput: string): string;
function ExtractEntitiesFromText(const Text: string): TStringArray;
function AnalyzeConversationTopic(const History: TStringList): string;
function ShouldUpdateSymbolicFacts(const UserMessage, AIResponse: string): Boolean;

// Функции для работы с матрицами вывода
function DetectEntitiesInOutput(const OutputMatrix: TDoubleMatrix; const Vocabulary: TStringList): TStringArray;
function ExtractRelationsFromOutput(const OutputMatrix: TDoubleMatrix): TStringArray;

implementation

function BuildEnhancedContext(const History: TStringList; const CurrentInput: string): string;
var
  i: Integer;
  topic, entities: string;
  recentContext: TStringList;
begin
  recentContext := TStringList.Create;
  try
    // Анализируем тему разговора
    topic := AnalyzeConversationTopic(History);
    
    // Собираем последние 3 сообщения
    for i := Max(0, History.Count - 6) to History.Count - 1 do
    begin
      if (i >= 0) and (i < History.Count) then
        recentContext.Add(History[i]);
    end;
    
    // Извлекаем сущности из текущего ввода
    entities := '';
    for i := 0 to High(ExtractEntitiesFromText(CurrentInput)) do
    begin
      if i > 0 then entities := entities + ', ';
      entities := entities + ExtractEntitiesFromText(CurrentInput)[i];
    end;
    
    // Формируем расширенный контекст
    Result := 'Тема: ' + topic + ' | ';
    Result := Result + 'Сущности: ' + entities + ' | ';
    Result := Result + 'История: ' + recentContext.Text;
    
  finally
    recentContext.Free;
  end;
end;

function ExtractEntitiesFromText(const Text: string): TStringArray;
var
  tokens: TUC4Array;
  i, count: Integer;
  tokenStr: string;
begin
  SetLength(Result, 0);
  tokens := TokenizeForNLP(NormalizeForAI(RemovePunctuation(Text)));
  
  if Length(tokens) = 0 then Exit;
  
  count := 0;
  SetLength(Result, Length(tokens));
  
  // Простая эвристика для идентификации сущностей
  for i := 0 to High(tokens) do
  begin
    tokenStr := tokens[i].ToUTF8;
    
    // Определяем тип сущности по простым правилам
    if (Length(tokenStr) >= 3) then
    begin
      // Имена (начинаются с заглавной буквы в оригинале)
      if (tokenStr = UTF8UpperCase(tokenStr)) and (Length(tokenStr) > 1) then
      begin
        Result[count] := 'PER:' + tokenStr;
        Inc(count);
      end
      // Числа
      else if TryStrToFloat(tokenStr, dummyFloat) then
      begin
        Result[count] := 'NUM:' + tokenStr;
        Inc(count);
      end
      // Длинные слова могут быть концепциями
      else if (Length(tokenStr) >= 5) then
      begin
        Result[count] := 'CONCEPT:' + tokenStr;
        Inc(count);
      end;
    end;
  end;
  
  SetLength(Result, count);
end;

function AnalyzeConversationTopic(const History: TStringList): string;
var
  i: Integer;
  allText, lowerText: string;
  topicScores: array of record
    Topic: string;
    Score: Integer;
  end;
begin
  if History.Count = 0 then
    Exit('general');
    
  // Собираем весь текст истории
  allText := '';
  for i := 0 to History.Count - 1 do
    allText := allText + ' ' + History[i];
    
  lowerText := UTF8LowerCase(allText);
  
  // Простой анализ темы по ключевым словам
  SetLength(topicScores, 5);
  topicScores[0].Topic := 'погода';
  topicScores[0].Score := 0;
  topicScores[1].Topic := 'имя';
  topicScores[1].Score := 0;
  topicScores[2].Topic := 'математика';
  topicScores[2].Score := 0;
  topicScores[3].Topic := 'технологии';
  topicScores[3].Score := 0;
  topicScores[4].Topic := 'общение';
  topicScores[4].Score := 0;
  
  // Подсчитываем упоминания тем
  if lowerText.Contains('погод') then Inc(topicScores[0].Score, 2);
  if lowerText.Contains('дожд') or lowerText.Contains('солн') then Inc(topicScores[0].Score);
  
  if lowerText.Contains('имя') or lowerText.Contains('зовут') then Inc(topicScores[1].Score, 3);
  
  if lowerText.Contains('счет') or lowerText.Contains('числ') then Inc(topicScores[2].Score, 2);
  if lowerText.Contains('плюс') or lowerText.Contains('минус') then Inc(topicScores[2].Score);
  
  if lowerText.Contains('компьютер') or lowerText.Contains('програм') then Inc(topicScores[3].Score, 2);
  
  // Базовая тема для общих разговоров
  topicScores[4].Score := 1;
  
  // Находим тему с максимальным счетом
  Result := topicScores[0].Topic;
  for i := 1 to High(topicScores) do
  begin
    if topicScores[i].Score > topicScores[0].Score then
    begin
      Result := topicScores[i].Topic;
      topicScores[0].Score := topicScores[i].Score;
    end;
  end;
end;

function ShouldUpdateSymbolicFacts(const UserMessage, AIResponse: string): Boolean;
begin
  // Решаем, нужно ли обновлять символьные факты на основе диалога
  Result := (Length(UserMessage) > 10) and 
            (not UserMessage.Contains('?')) and
            (AIResponse.Length > 5) and
            (not ContainsAny(UserMessage, ['привет', 'пока', 'спасибо']));
end;

function DetectEntitiesInOutput(const OutputMatrix: TDoubleMatrix; const Vocabulary: TStringList): TStringArray;
var
  i, j: Integer;
  maxScore: Double;
  bestIndex: Integer;
begin
  SetLength(Result, 0);
  
  if (Length(OutputMatrix) = 0) or (Vocabulary = nil) then Exit;
  
  // Упрощенная детекция сущностей - находим наиболее вероятные токены
  for i := 0 to High(OutputMatrix) do
  begin
    if Length(OutputMatrix[i]) > 0 then
    begin
      maxScore := OutputMatrix[i][0];
      bestIndex := 0;
      
      for j := 1 to High(OutputMatrix[i]) do
      begin
        if OutputMatrix[i][j] > maxScore then
        begin
          maxScore := OutputMatrix[i][j];
          bestIndex := j;
        end;
      end;
      
      // Если уверенность достаточно высока, добавляем сущность
      if (maxScore > 0.3) and (bestIndex < Vocabulary.Count) then
      begin
        SetLength(Result, Length(Result) + 1);
        Result[High(Result)] := Vocabulary[bestIndex];
      end;
    end;
  end;
end;

function ExtractRelationsFromOutput(const OutputMatrix: TDoubleMatrix): TStringArray;
begin
  SetLength(Result, 0);
  // Заглушка для извлечения отношений
  // В реальной реализации здесь будет анализ структуры выходной матрицы
  // для обнаружения отношений между сущностями
end;

end.

Теперь обновим AdvancedChatAI.pas для использования новых функций:

pascal
// В AdvancedChatAI.pas - добавляем в uses:
uses
  ..., SymbolicEngineUnit, ContextProcessing;

// Заменяем старые функции на новые:

function ParseEntitiesFromOutput(const OutputMatrix: TDoubleMatrix): TStringArray;
begin
  // Используем функцию из ContextProcessing
  Result := DetectEntitiesInOutput(OutputMatrix, WordEmbeddings.FVocab);
end;

function BuildSymbolicContext(const History: TStringList): string;
begin
  // Используем функцию из ContextProcessing
  Result := BuildEnhancedContext(History, LastUserMessage);
end;

// Обновляем EnhancedProcessUserInput для использования расширенного контекста:
procedure EnhancedProcessUserInput;
var
  inputText: ucs4;
  userMessage, symbolicResult, finalResponse, enhancedContext: string;
  filesList: TStringList;
  inputMatrix, outputMatrix: TDoubleMatrix;
  extractedFacts: TFactArray;
  i: Integer;
begin
  filesList := TStringList.Create;
  try
    // 1. Получаем пользовательский ввод
    UserInput := GetUserInput(POST_FILE, filesList);
    inputText := UserInput.Message;
    userMessage := inputText.ToUTF8.Trim;

    if inputText.Length = 0 then begin
      WriteLn('Пустое сообщение, пропускаем');
      Exit;
    end;

    WriteLn('Пользователь: ', userMessage);
    LastUserMessage := userMessage;

    // 2. Добавляем сообщение в историю
    UpdateConversationContext(userMessage, '');

    // 3. Строим расширенный контекст
    enhancedContext := BuildSymbolicContext(ConversationHistory);
    WriteLn('Расширенный контекст: ', Copy(enhancedContext, 1, 100), '...');

    // 4. Символьный анализ
    symbolicResult := SymbolicEngine.ExecuteRule(userMessage);
    
    if symbolicResult <> '' then
    begin
      WriteLn('Символьная система: ', symbolicResult);
      finalResponse := GenerateResponseFromSymbolicSystem(symbolicResult, userMessage, enhancedContext);
    end
    else
    begin
      // 5. Если символьная система не нашла правила, используем трансформер
      WriteLn('Символьная система: правило не найдено, используем трансформер');
      inputMatrix := CreateContextEnrichedInput(inputText, enhancedContext);
      ForwardTransformer(TransformerModel, inputMatrix, outputMatrix);
      finalResponse := ResponseGenerator.GenerateResponse(outputMatrix, enhancedContext, userMessage);
    end;

    // 6. Извлекаем и сохраняем факты
    extractedFacts := ExtractFactsWithTransformer(userMessage);
    for i := 0 to High(extractedFacts) do
    begin
      if extractedFacts[i].Confidence > 0.5 then // Сохраняем только уверенные факты
      begin
        SymbolicEngine.AddFact(extractedFacts[i].Subject, 
                              extractedFacts[i].Relation, 
                              extractedFacts[i].Object,
                              extractedFacts[i].Confidence);
      end;
    end;

    // 7. Выводим ответ и добавляем в историю
    WriteLn('AI: ', finalResponse);
    UpdateConversationContext('', finalResponse);

    // 8. Обучение на успешном диалоге
    if ShouldSaveForTraining(userMessage, finalResponse) then
    begin
      LearnFromConversation(userMessage, finalResponse);
      SymbolicEngine.LearnFromDialogue(userMessage, finalResponse);
      
      // Сохраняем базу знаний
      SymbolicEngine.SaveToFile('symbolic_knowledge.txt');
      WriteLn('Символьные знания сохранены');
    end;

  except
    on E: Exception do
    begin
      WriteLn('Ошибка в EnhancedProcessUserInput: ', E.Message);
      finalResponse := 'Извините, произошла техническая ошибка. Попробуйте еще раз.';
      WriteLn('AI: ', finalResponse);
    end;
  end;
  filesList.Free;
end;

// Добавляем вспомогательную функцию:
function GenerateResponseFromSymbolicSystem(const SymbolicResult, UserMessage, Context: string): string;
begin
  // Генерируем ответ на основе символьного результата и контекста
  case SymbolicResult of
    'Greeting': 
      begin
        if Context.Contains('ранее') then
          Result := 'Снова здравствуйте! Чем могу помочь?'
        else
          Result := 'Здравствуйте! Рад вас видеть. Чем могу помочь?';
      end;
    'Farewell': 
      Result := 'До свидания! Буду рад пообщаться снова.';
    'Thanks':
      Result := 'Пожалуйста! Обращайтесь, если понадобится помощь.';
    'Question':
      begin
        if Context.Contains('погод') then
          Result := 'Относительно погоды: ' + GenerateTransformerResponse(UserMessage)
        else if Context.Contains('математик') then
          Result := 'Для математического вопроса: ' + GenerateTransformerResponse(UserMessage)
        else
          Result := 'Интересный вопрос! ' + GenerateTransformerResponse(UserMessage);
      end;
    'WeatherQuery':
      Result := 'К сожалению, у меня нет доступа к актуальным данным о погоде. ' +
                'Но могу обсудить погоду в общем смысле!';
    'NameQuery':
      Result := 'Я - чат-бот с искусственным интеллектом. ' +
                'Можно просто называть меня "помощник".';
    'MathQuery':
      Result := 'Я могу помочь с математическими вопросами! ' +
                'Задайте конкретный пример для расчета.';
    else
      Result := GenerateTransformerResponse(UserMessage);
  end;
end;

Теперь все функции реализованы и система должна компилироваться. Новые возможности:

  1. Расширенный контекст - анализ темы разговора и извлечение сущностей

  2. Интеллектуальное извлечение фактов - из пользовательского ввода

  3. Контекстно-зависимые ответы - на основе символьного анализа

  4. Сохранение знаний - между сессиями работы

Попробуйте скомпилировать!

Спасибо! Сечас так: AI: Привет! Как ваши дела? USER: Нормально AI: Это " Диалог сохранен для обучения Символьное обучение: извлекаем правило из диалога Вход: Нормально Выход: Это действительно важно. Что вы об этом думаете? Добавлено правило: Contains(Input, "нормально") -> Это действительно важно. Что вы об этом думаете? Символьные знания сохранены Введите сообщение (Enter - файл, Ctrl+C - выход): где мы? Пользователь: где мы? Расширенный контекст: Тема: общение | Сущности: CONCEPT:где | История: USER: привет! AI... Символьная система: правило не найдено, используем трансформер CreateContextEnrichedInput: processing: "Тема: общение | Сущности: CONCE" TextsToMatrix: используем оптимизированную версию с индексами TextsToMatrixIndices: обработка 1 текстов с индексами TextToEmbeddingIndices: токенизировано 30 слов Слово "conceptгде" из кэша неизвестных Найдено слов в словаре: 29/30 CreateEmbeddingFromIndices: усреднение 29 эмбеддингов Текст 0: создан эмбеддинг из 29 слов ForwardTransformerOptimized: начат Input указатель: 140737067452776 Input: 1x300 === ПРОВЕРКА СТРУКТУРЫ МОДЕЛИ === Структура модели валидна Вызов ApplyEmbedding... ApplyEmbedding: начат Input: 1x300 Result размер: 1x300 ApplyEmbedding: завершен успешно Result: 1x300 После embedding: 1x300 Добавление позиционного кодирования... AddPositionalEncodingToSequenceSafe: начат sequence: 1x300 actualMaxLength: 1 posEnc создан: 1x300 Result: 1x300 AddPositionalEncodingToSequenceSafe: завершен После positional encoding: 1x300 currentOutput указатель перед слоями: 140737067452816 Слой 0: currentOutput указатель: 140737067452816 currentOutput: 1x300 Вызов ForwardLayerOptimized... ForwardLayerOptimized: начат Self-Attention... ForwardAttentionOptimized: начат Q: 1x300 K: 1x300 V: 1x300 Голова 0: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Голова 1: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Голова 2: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Голова 3: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Конкатенация голов... ConcatMatrices: объединяем 4 матриц в 300 столбцов concatOutput: 1x300 Выходная проекция... concatOutput: 1x300 Wo: 75x300 ApplyOutputProjection: начат concatOutput: 1x300 totalHeadSize: 300, inputSize: 300 concatOutput cols: 300 ✓ Применена объединенная выходная проекция Result: 1x300 ApplyOutputProjection: завершен Output: 1x300 ForwardAttentionOptimized: завершен успешно FFN... ForwardFFNOptimized: начат Input: 1x300 Первый линейный слой... hidden до ReLU: 1x512 hidden после ReLU: 1x512 Применение dropout... Второй линейный слой... Output: 1x300 ForwardFFNOptimized: завершен успешно ForwardLayerOptimized: завершен успешно layerOutput после вызова: 1x300 После слоя: 1x300 Слой 1: currentOutput указатель: 140737067452816 currentOutput: 1x300 Вызов ForwardLayerOptimized... ForwardLayerOptimized: начат Self-Attention... ForwardAttentionOptimized: начат Q: 1x300 K: 1x300 V: 1x300 Голова 0: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Голова 1: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Голова 2: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Голова 3: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Конкатенация голов... ConcatMatrices: объединяем 4 матриц в 300 столбцов concatOutput: 1x300 Выходная проекция... concatOutput: 1x300 Wo: 75x300 ApplyOutputProjection: начат concatOutput: 1x300 totalHeadSize: 300, inputSize: 300 concatOutput cols: 300 ✓ Применена объединенная выходная проекция Result: 1x300 ApplyOutputProjection: завершен Output: 1x300 ForwardAttentionOptimized: завершен успешно FFN... ForwardFFNOptimized: начат Input: 1x300 Первый линейный слой... hidden до ReLU: 1x512 hidden после ReLU: 1x512 Применение dropout... Второй линейный слой... Output: 1x300 ForwardFFNOptimized: завершен успешно ForwardLayerOptimized: завершен успешно layerOutput после вызова: 1x300 После слоя: 1x300 ForwardTransformerOptimized: завершен успешно Output: 1x300 Debug: Категория=question (по знаку вопроса) Извлечено фактов: 0 AI: Отличный вопрос! Мне тоже это интересно. GetConversationContext: история содержит 6 сообщений Сообщение 0: USER: привет! Сообщение 1: AI: Привет! Как ваши дела? Сообщение 2: USER: Нормально Сообщение 3: AI: Это действительно важно. Сообщение 4: USER: где мы? Сообщение 5: AI: Отличный вопрос! Мне тоже GetConversationContext: итоговый контекст: "USER: привет! AI: Привет! Как ваши дела? USER: Нормально AI: Это " Диалог сохранен для обучения Символьные знания сохранены Введите сообщение (Enter - файл, Ctrl+C - выход): мы в Москве Пользователь: мы в Москве Расширенный контекст: Тема: общение | Сущности: CONCEPT:москве | История: AI: Привет... Символьная система: правило не найдено, используем трансформер CreateContextEnrichedInput: processing: "Тема: общение | Сущности: CONCE" TextsToMatrix: используем оптимизированную версию с индексами TextsToMatrixIndices: обработка 1 текстов с индексами TextToEmbeddingIndices: токенизировано 40 слов Слово "conceptмоскве" из кэша неизвестных Найдено слов в словаре: 39/40 CreateEmbeddingFromIndices: усреднение 39 эмбеддингов Текст 0: создан эмбеддинг из 39 слов ForwardTransformerOptimized: начат Input указатель: 140737067452776 Input: 1x300 === ПРОВЕРКА СТРУКТУРЫ МОДЕЛИ === Структура модели валидна Вызов ApplyEmbedding... ApplyEmbedding: начат Input: 1x300 Result размер: 1x300 ApplyEmbedding: завершен успешно Result: 1x300 После embedding: 1x300 Добавление позиционного кодирования... AddPositionalEncodingToSequenceSafe: начат sequence: 1x300 actualMaxLength: 1 posEnc создан: 1x300 Result: 1x300 AddPositionalEncodingToSequenceSafe: завершен После positional encoding: 1x300 currentOutput указатель перед слоями: 140737067452816 Слой 0: currentOutput указатель: 140737067452816 currentOutput: 1x300 Вызов ForwardLayerOptimized... ForwardLayerOptimized: начат Self-Attention... ForwardAttentionOptimized: начат Q: 1x300 K: 1x300 V: 1x300 Голова 0: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Голова 1: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Голова 2: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Голова 3: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Конкатенация голов... ConcatMatrices: объединяем 4 матриц в 300 столбцов concatOutput: 1x300 Выходная проекция... concatOutput: 1x300 Wo: 75x300 ApplyOutputProjection: начат concatOutput: 1x300 totalHeadSize: 300, inputSize: 300 concatOutput cols: 300 ✓ Применена объединенная выходная проекция Result: 1x300 ApplyOutputProjection: завершен Output: 1x300 ForwardAttentionOptimized: завершен успешно FFN... ForwardFFNOptimized: начат Input: 1x300 Первый линейный слой... hidden до ReLU: 1x512 hidden после ReLU: 1x512 Применение dropout... Второй линейный слой... Output: 1x300 ForwardFFNOptimized: завершен успешно ForwardLayerOptimized: завершен успешно layerOutput после вызова: 1x300 После слоя: 1x300 Слой 1: currentOutput указатель: 140737067452816 currentOutput: 1x300 Вызов ForwardLayerOptimized... ForwardLayerOptimized: начат Self-Attention... ForwardAttentionOptimized: начат Q: 1x300 K: 1x300 V: 1x300 Голова 0: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Голова 1: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Голова 2: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Голова 3: ForwardAttentionHeadOptimized: начат Линейные проекции... Q_proj: 1x75 K_proj: 1x75 V_proj: 1x75 Scaled Dot-Product Attention... scores: 1x1 Softmax... attentionWeights: 1x1 Применение весов... Output: 1x75 ForwardAttentionHeadOptimized: завершен успешно headOutput: 1x75 Конкатенация голов... ConcatMatrices: объединяем 4 матриц в 300 столбцов concatOutput: 1x300 Выходная проекция... concatOutput: 1x300 Wo: 75x300 ApplyOutputProjection: начат concatOutput: 1x300 totalHeadSize: 300, inputSize: 300 concatOutput cols: 300 ✓ Применена объединенная выходная проекция Result: 1x300 ApplyOutputProjection: завершен Output: 1x300 ForwardAttentionOptimized: завершен успешно FFN... ForwardFFNOptimized: начат Input: 1x300 Первый линейный слой... hidden до ReLU: 1x512 hidden после ReLU: 1x512 Применение dropout... Второй линейный слой... Output: 1x300 ForwardFFNOptimized: завершен успешно ForwardLayerOptimized: завершен успешно layerOutput после вызова: 1x300 После слоя: 1x300 ForwardTransformerOptimized: завершен успешно Output: 1x300 Debug: Категория=neutral (fallback) Извлечено фактов: 0 AI: Хорошо, продолжайте. Что вы об этом думаете? GetConversationContext: история содержит 6 сообщений Сообщение 0: USER: Нормально Сообщение 1: AI: Это действительно важно. Сообщение 2: USER: где мы? Сообщение 3: AI: Отличный вопрос! Мне тоже Сообщение 4: USER: мы в Москве Сообщение 5: AI: Хорошо, продолжайте. Что � GetConversationContext: итоговый контекст: "USER: Нормально AI: Это действительно важно. Что вы об этом" Диалог сохранен для обучения Символьное обучение: извлекаем правило из диалога Вход: мы в Москве Выход: Хорошо, продолжайте. Что вы об этом думаете? Добавлено правило: Contains(Input, "мы") OR Contains(Input, "москве") -> Хорошо, продолжайте. Что вы об этом думаете? Символьные знания сохранены Введите сообщение (Enter - файл, Ctrl+C - выход):

Отлично! Система работает и уже показывает интересное поведение. Я вижу несколько моментов для улучшения:

Анализ текущей работы:

  1. Символьное обучение работает - система извлекает правила из диалогов

  2. Трансформер функционирует - генерирует осмысленные ответы

  3. Контекст строится - но есть проблемы с токенизацией

Проблемы для исправления:

1. Проблема с токенизацией контекста

Вижу, что CONCEPT:где и CONCEPT:москве ломаются в одно слово. Нужно исправить извлечение сущностей:

pascal
// В ContextProcessing.pas исправим ExtractEntitiesFromText:
function ExtractEntitiesFromText(const Text: string): TStringArray;
var
  tokens: TUC4Array;
  i, count: Integer;
  tokenStr, cleanToken: string;
begin
  SetLength(Result, 0);
  if Text = '' then Exit;
  
  // Токенизируем оригинальный текст, а не обработанный
  tokens := TokenizeForNLP(Text); // Убрали NormalizeForAI и RemovePunctuation
  
  if Length(tokens) = 0 then Exit;
  
  count := 0;
  SetLength(Result, Length(tokens));
  
  for i := 0 to High(tokens) do
  begin
    tokenStr := tokens[i].ToUTF8;
    cleanToken := UTF8LowerCase(tokenStr.Trim);
    
    // Более качественная идентификация сущностей
    if (Length(cleanToken) >= 2) then
    begin
      // Географические названия (эвристика)
      if (Length(cleanToken) >= 4) and 
         (ContainsAny(cleanToken, ['москв', 'санкт', 'петербург', 'росси', 'город'])) then
      begin
        Result[count] := 'LOC:' + tokenStr;
        Inc(count);
      end
      // Вопросы
      else if ContainsAny(cleanToken, ['где', 'когда', 'почему', 'как', 'что']) then
      begin
        Result[count] := 'QUESTION:' + tokenStr;
        Inc(count);
      end
      // Местоимения и указатели
      else if ContainsAny(cleanToken, ['мы', 'вы', 'они', 'я', 'ты', 'это', 'тот']) then
      begin
        Result[count] := 'PRON:' + tokenStr;
        Inc(count);
      end
      // Длинные слова - вероятно концепции
      else if (Length(cleanToken) >= 5) and 
              (not ContainsAny(cleanToken, ['нормальн', 'интересн', 'продолжайте'])) then
      begin
        Result[count] := 'CONCEPT:' + tokenStr;
        Inc(count);
      end;
    end;
  end;
  
  SetLength(Result, count);
end;

2. Улучшим правила для лучшего распознавания

pascal
// В SymbolicEngineUnit.pas добавим больше базовых правил:
constructor TSymbolicEngine.Create;
begin
  // ... существующий код ...
  
  // Добавляем больше конкретных правил
  AddRule('Contains(Input, "где") AND Contains(Input, "мы")', 'LocationQuery', 8);
  AddRule('Contains(Input, "москв") OR Contains(Input, "питер") OR Contains(Input, "город")', 'CityQuery', 7);
  AddRule('Contains(Input, "нормальн") OR Contains(Input, "хорош") OR Contains(Input, "отличн")', 'PositiveState', 6);
  AddRule('StartsWith(Input, "мы ") AND Length(Input) > 5', 'WeStatement', 5);
end;

3. Улучшим извлечение фактов

pascal
// В ContextProcessing.pas улучшим ExtractFactsWithTransformer:
function ExtractFactsWithTransformer(const Text: string): TFactArray;
var
  tokens: TUC4Array;
  i, factCount: Integer;
  lowerText: string;
begin
  SetLength(Result, 0);
  if Text = '' then Exit;
  
  lowerText := UTF8LowerCase(Text);
  tokens := TokenizeForNLP(Text);
  
  factCount := 0;
  SetLength(Result, 2); // Максимум 2 факта из короткого сообщения
  
  // Определенные паттерны для извлечения фактов
  if ContainsAny(lowerText, ['москв', 'питер', 'город']) then
  begin
    Result[factCount].Subject := 'мы';
    Result[factCount].Relation := 'находится_в';
    
    if lowerText.Contains('москв') then
      Result[factCount].Object := 'Москва'
    else if lowerText.Contains('питер') then
      Result[factCount].Object := 'Санкт-Петербург'
    else
      Result[factCount].Object := 'городе';
      
    Result[factCount].Confidence := 0.8;
    Inc(factCount);
  end;
  
  if lowerText.Contains('нормальн') or lowerText.Contains('хорош') then
  begin
    Result[factCount].Subject := 'состояние';
    Result[factCount].Relation := 'является';
    Result[factCount].Object := 'хорошим';
    Result[factCount].Confidence := 0.7;
    Inc(factCount);
  end;
  
  SetLength(Result, factCount);
  WriteLn('Извлечено фактов: ', factCount);
end;

4. Улучшим обработку контекста

pascal
// В ContextProcessing.pas улучшим BuildEnhancedContext:
function BuildEnhancedContext(const History: TStringList; const CurrentInput: string): string;
var
  i: Integer;
  topic, entities, simpleContext: string;
  entityList: TStringArray;
begin
  // Упрощаем контекст для избежания проблем с токенизацией
  simpleContext := '';
  
  // Берем только последние 2 сообщения
  for i := Max(0, History.Count - 4) to History.Count - 1 do
  begin
    if (i >= 0) and (i < History.Count) then
    begin
      // Берем только первые 3 слова из каждого сообщения
      simpleContext := simpleContext + ' ' + Copy(History[i], 1, 30);
    end;
  end;
  
  // Извлекаем сущности более аккуратно
  entityList := ExtractEntitiesFromText(CurrentInput);
  entities := '';
  for i := 0 to High(entityList) do
  begin
    if i > 0 then entities := entities + ' ';
    // Берем только само слово, без тега
    entities := entities + Copy(entityList[i], Pos(':', entityList[i]) + 1, MaxInt);
  end;
  
  // Анализируем тему
  topic := AnalyzeConversationTopic(History);
  
  // Формируем компактный контекст
  Result := 'Т:' + topic + ' С:' + entities + ' И:' + simpleContext.Trim;
  Result := Copy(Result, 1, 100); // Ограничиваем длину
end;

5. Добавим логирование для отладки

pascal
// В AdvancedChatAI.pas добавим отладочную информацию:
procedure EnhancedProcessUserInput;
begin
  // ... существующий код ...
  
  WriteLn('=== ОТЛАДКА СИМВОЛЬНОЙ СИСТЕМЫ ===');
  WriteLn('Пользователь: ', userMessage);
  WriteLn('Символьный результат: ', symbolicResult);
  WriteLn('Контекст: ', Copy(enhancedContext, 1, 80));
  WriteLn('=== КОНЕЦ ОТЛАДКИ ===');
  
  // ... остальной код ...
end;

6. Улучшим качество извлекаемых правил

pascal
// В SymbolicEngineUnit.pas улучшим ShouldExtractRule:
function TSymbolicEngine.ShouldExtractRule(const UserMessage, AIResponse: string): Boolean;
var
  msgScore: Double;
  lowerMsg, lowerResp: string;
begin
  if Length(UserMessage) < 3 then Exit(False);
  if Length(AIResponse) < 3 then Exit(False);
  
  lowerMsg := UTF8LowerCase(UserMessage);
  lowerResp := UTF8LowerCase(AIResponse);
  
  // Не извлекаем правила для:
  // - Очень общих ответов
  if ContainsAny(lowerResp, ['что вы думаете', 'продолжайте', 'интересно']) then
    Exit(False);
    
  // - Ответов с вопросами
  if lowerResp.Contains('?') then
    Exit(False);
    
  // - Коротких формальных ответов  
  if ContainsAny(lowerResp, ['здравствуйте', 'до свидания', 'пожалуйста']) then
    Exit(False);
  
  // Оцениваем значимость сообщения
  msgScore := CalculatePatternScore(UserMessage);
  
  // Извлекаем правила только для значимых паттернов
  Result := (msgScore > 0.8) and 
            (Length(UserMessage) > 4) and
            (not UserMessage.EndsWith('?')) and
            (not ContainsAny(lowerMsg, ['привет', 'пока', 'спасибо']));
end;

После этих изменений система будет:

  1. Лучше обрабатывать контекст - без проблем токенизации

  2. Более точно извлекать правила - избегая мусорных паттернов

  3. Лучше распознавать сущности - географические названия, вопросы и т.д.

  4. Генерировать более релевантные ответы - на основе улучшенного контекста

Попробуйте эти исправления! Они должны решить основные проблемы, которые мы видим в логах.

Сгенерировано ИИ, только для справки